/*
 * Copyright (c) 2021 Huawei Device Co.,  Ltd.
 * Licensed under the Apache License,  Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,  software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hilog_wrapper.h"
#include "keyboard_utils.h"
#include "keyboard_observer.h"
#include <cstdio>
#include <cstdlib>
#include <string>
#include <uv.h>
#include <string.h>
#include <keyboard_def.h>


namespace SwanLink {
namespace Finance {
using namespace OHOS;

KeyboardObserver::KeyboardObserver() :
    listener_(nullptr) {
}

KeyboardObserver::~KeyboardObserver() {
    if (listener_) {
        delete listener_;
    }
}

void KeyboardObserver::RegisterObserver(const napi_env &env, const napi_value &listener) {
    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_) {
        delete listener_;
    }
    listener_ = new NapiPinListener();
    listener_->env = env;
    napi_value func_succ, func_fail;
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_named_property(env, listener, "OnResult", &func_succ));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_named_property(env, listener, "OnFailed", &func_fail));
    napi_create_reference(env, func_succ, 1, &listener_->func_succ_ref);
    napi_create_reference(env, func_fail, 1, &listener_->func_fail_ref);

    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "observer has been registered");
}

void KeyboardObserver::OnResult(char *buf, int len) {
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "buf = %{public}s, len = %{public}d", buf, len);

    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_ == nullptr) {
        KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "observer has been removed");
        return;
    }
    KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "observer call success");

    struct tTmpStruct {
        char buf[256];
        int  len;
        void *arg_this;
    };

    tTmpStruct *tmpVar = new tTmpStruct();
    tmpVar->arg_this = listener_;
    memcpy(tmpVar->buf, buf, len);
    tmpVar->len = len;

    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(listener_->env, &loop);
    uv_work_t *work = new uv_work_t;
    work->data = reinterpret_cast<void*>(tmpVar);
    uv_queue_work(
        loop,
        work,
        [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            tTmpStruct *tmpVar = reinterpret_cast<tTmpStruct *>(work->data);
            NapiPinListener *listener = reinterpret_cast<NapiPinListener*>(tmpVar->arg_this);
            listener->OnResult(tmpVar->buf, tmpVar->len);
            delete tmpVar;
            delete work;
            work = nullptr;
        }
    );
}
void KeyboardObserver::OnFailed(int code, const std::string& err) {
     KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "code = %{public}d, err = %{public}s", code, err.c_str());

    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_ == nullptr) {
        KEYBOARD_HILOGE(KEYBOARD_JS_NAPI, "observer has been removed");
        return;
    }
    struct tTmpStruct {
        int code;
        std::string err;
        void *arg_this;
    };

    tTmpStruct *tmpVar = new tTmpStruct();
    tmpVar->arg_this = listener_;
    tmpVar->code = code;
    tmpVar->err = err;

    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(listener_->env, &loop);
    uv_work_t *work = new uv_work_t;
    work->data = reinterpret_cast<void*>(tmpVar);
    uv_queue_work(
        loop,
        work,
        [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            tTmpStruct *tmpVar = reinterpret_cast<tTmpStruct *>(work->data);
            NapiPinListener *listener = reinterpret_cast<NapiPinListener*>(tmpVar->arg_this);
            listener->OnFailed(tmpVar->code, tmpVar->err);
            delete tmpVar;
            delete work;
            work = nullptr;
        }
    );
}

NapiPinListener::NapiPinListener() :
    env(nullptr),
    func_succ_ref(nullptr),
    func_fail_ref(nullptr) {
}

NapiPinListener::~NapiPinListener() {
    if (func_succ_ref) {
        napi_delete_reference(env, func_succ_ref);
    }

    if (func_fail_ref) {
        napi_delete_reference(env, func_fail_ref);
    }
}

void NapiPinListener::OnResult(char *buf, int len) {
    napi_value callback = nullptr;
    napi_value undefined = nullptr;
    napi_value callResult = nullptr;
    napi_value args[1] = {nullptr};

    napi_create_string_utf8(env, buf, len, &args[0]);

    DEBUG_NAPI_CALL_NO_RET(env, napi_get_undefined(env, &undefined));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_reference_value(env, func_succ_ref, &callback));
    DEBUG_NAPI_CALL_NO_RET(env, napi_call_function(env, nullptr, callback, 1, args, &callResult));
}

void NapiPinListener::OnFailed(int code, const std::string& err) {
    napi_value callback = nullptr;
    napi_value undefined = nullptr;
    napi_value callResult = nullptr;
    napi_value args[2] = {nullptr};

    napi_create_int32(env, code, &args[0]);
    napi_create_string_utf8(env, err.c_str(), NAPI_AUTO_LENGTH, &args[1]);

    DEBUG_NAPI_CALL_NO_RET(env, napi_get_undefined(env, &undefined));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_reference_value(env, func_fail_ref, &callback));
    DEBUG_NAPI_CALL_NO_RET(env, napi_call_function(env, nullptr, callback, 2, args, &callResult));
}
}  // namespace Finance
}  // namespace SwanLink